package com.sevntu.apvs.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileNotFoundException;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;
import com.sevntu.apvs.dModel.CircuitLoader;
import com.sevntu.apvs.dModel.CircuitTestLoader;
import com.sevntu.apvs.dModel.DModel;
import com.sevntu.apvs.dModel.DModelException;
import com.sevntu.apvs.modeling.Element_Model;
import com.sevntu.apvs.modeling.Modeling;
import com.sevntu.apvs.timeChart.TimeChartGUI;

public class GUI extends JFrame {

	private static final long serialVersionUID = 1L;

	/** Use for log message generation */
	private static final Logger LOG = Logger.getLogger(GUI.class.getName());

	private static final String TEMP_MODEL = "tempModel.xml";
	private static final String TEMP_TEST = "tempTest.txt";
	private static final String RESULT_FOLDER = "protocols";
	private static final String PROTOCOL_MODELING = RESULT_FOLDER + File.separatorChar + "modelingProtocol.txt";
	private static final String PROTOCOL_ANALYSE = RESULT_FOLDER + File.separatorChar + "anlyseProtocol.txt";

	private File lastDir;
	private final JFileChooser fileChooser;
	private final GUIContentProvider contentProvider;

	private DModel dModel;
	private int[][] circuitTests;
	private final CircuitLoader circuitLoader;
	private final CircuitTestLoader circuitTestLoader;

	private static final ImageIcon IMG_FILE_OPEN = new ImageIcon("icons\\open.png");
	private static final ImageIcon IMG_FILE_SAVE = new ImageIcon("icons\\save.png");
	private static final ImageIcon IMG_CLOSE = new ImageIcon("icons\\close.png");
	private static final ImageIcon IMG_INFO = new ImageIcon("icons\\info.png");

	private JTextArea textAreaModel;
	private JTextArea textAreaTest;
	private JTextArea textAreaResults;
	private JButton btnModeling;
	private JButton btnTimeChart;
	private JButton btnResultsAnalize;
	private JButton btnSystemState;
	private JButton btnCreateDModel;

	/**
	 * Create the application.
	 */
	public GUI() {
		circuitLoader = new CircuitLoader();
		lastDir = new File("Input");
		fileChooser = new JFileChooser();
		contentProvider = new GUIContentProvider();
		circuitTestLoader = new CircuitTestLoader(TEMP_TEST);
		initializeGUI();
		confFileChooser(fileChooser);
	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		PropertyConfigurator.configure("config/log4j.properties");
		EventQueue.invokeLater(new Runnable() {

			@Override
			public void run() {
				try {
					// Set System L&F
					UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
					GUI window = new GUI();
					window.setVisible(true);
				} catch (UnsupportedLookAndFeelException e) {
					LOG.warn(e);
				} catch (ClassNotFoundException e) {
					LOG.warn(e);
				} catch (InstantiationException e) {
					LOG.warn(e);
				} catch (IllegalAccessException e) {
					LOG.warn(e);
				} catch (Exception e) {
					LOG.warn(e);
				}
			}
		});
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initializeGUI() {
		setWindowSize(900, 600);
		setMinimumSize(new Dimension(450, 300));
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {

				// This closes the window and terminates the
				// Java Virtual Machine in the event that the
				// Frame is closed by clicking on X.
				exit();
			}
		});
//		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setTitle("Anisochronous Modeling");
		FormLayout formLayout = new FormLayout(new ColumnSpec[] { FormFactory.RELATED_GAP_COLSPEC,
				ColumnSpec.decode("50dlu:grow(5)"), ColumnSpec.decode("pref:grow"), FormFactory.RELATED_GAP_COLSPEC, },
				new RowSpec[] { FormFactory.RELATED_GAP_ROWSPEC, RowSpec.decode("10dlu:grow"),
						RowSpec.decode("pref:grow"), FormFactory.RELATED_GAP_ROWSPEC, });
		getContentPane().setLayout(formLayout);

		JPanel panel = new JPanel();
		getContentPane().add(panel, "2, 2, 1, 2, fill, fill");
		FormLayout fl_panel = new FormLayout(new ColumnSpec[] { ColumnSpec.decode("50dlu:grow(3)"),
				ColumnSpec.decode("default:grow(2)"), }, new RowSpec[] { RowSpec.decode("pref:grow"),
				RowSpec.decode("pref:grow"), RowSpec.decode("pref:grow"), });
		panel.setLayout(fl_panel);

		JScrollPane scrollPaneModel = new JScrollPane();
		scrollPaneModel.setBorder(null);
		scrollPaneModel.setViewportBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Model",
				TitledBorder.LEADING, TitledBorder.TOP, null, new Color(51, 94, 168)));
		panel.add(scrollPaneModel, "1, 1, 1, 3, fill, fill");

		textAreaModel = new JTextArea();
		textAreaModel.setTabSize(5);
		textAreaModel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		textAreaModel.setFont(new Font("Monospaced", Font.PLAIN, 12));
		textAreaModel.setColumns(5);
		textAreaModel.setRows(2);
		scrollPaneModel.setViewportView(textAreaModel);

		JScrollPane scrollPaneTest = new JScrollPane();
		scrollPaneTest.setBorder(null);
		panel.add(scrollPaneTest, "2, 1, 1, 3, fill, fill");
		scrollPaneTest.setViewportBorder(new TitledBorder(null, "Test", TitledBorder.LEADING, TitledBorder.TOP, null,
				null));

		textAreaTest = new JTextArea();
		textAreaTest.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		textAreaTest.setRows(2);
		textAreaTest.setFont(new Font("Monospaced", Font.PLAIN, 12));
		scrollPaneTest.setViewportView(textAreaTest);

		JPanel panelControl = new JPanel();
		getContentPane().add(panelControl, "3, 2, fill, fill");
		panelControl.setLayout(new FormLayout(new ColumnSpec[] { ColumnSpec.decode("pref:grow"),
				FormFactory.PREF_COLSPEC, ColumnSpec.decode("pref:grow"), }, new RowSpec[] {
				RowSpec.decode("pref:grow"), RowSpec.decode("pref:grow"), RowSpec.decode("pref:grow"),
				RowSpec.decode("pref:grow"), RowSpec.decode("pref:grow"), }));

		btnCreateDModel = new JButton("Create DModel");
		btnCreateDModel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				btnCreateDModelAction();
			}
		});
		btnCreateDModel.setMnemonic('D');
		panelControl.add(btnCreateDModel, "2, 1, fill, center");

		btnModeling = new JButton("Modeling");
		btnModeling.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				btnModelingAction();
			}
		});
		btnModeling.setMnemonic('M');
		panelControl.add(btnModeling, "2, 2, fill, center");
		btnModeling.setEnabled(false);

		btnTimeChart = new JButton("Time Charts");
		btnTimeChart.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				btnTimeChartAction();
			}
		});
		btnTimeChart.setMnemonic('T');
		panelControl.add(btnTimeChart, "2, 3, fill, center");
		btnTimeChart.setEnabled(false);

		btnResultsAnalize = new JButton("Results Analize");
		btnResultsAnalize.setMnemonic('R');
		btnResultsAnalize.setEnabled(false);
		panelControl.add(btnResultsAnalize, "2, 4");

		btnSystemState = new JButton("System State");
		btnSystemState.setMnemonic('S');
		btnSystemState.setEnabled(false);
		panelControl.add(btnSystemState, "2, 5");

		JScrollPane scrollPaneResults = new JScrollPane();
		scrollPaneResults.setBorder(null);
		scrollPaneResults.setViewportBorder(new TitledBorder(null, "Status Info", TitledBorder.LEADING,
				TitledBorder.TOP, null, null));
		getContentPane().add(scrollPaneResults, "3, 3, fill, fill");

		textAreaResults = new JTextArea();
		textAreaResults.setLineWrap(true);
		textAreaResults.setColumns(4);
		textAreaResults.setWrapStyleWord(true);
		textAreaResults.setForeground(Color.DARK_GRAY);
		textAreaResults.setEditable(false);
		textAreaResults.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		textAreaResults.setFont(new Font("Monospaced", Font.PLAIN, 12));
		scrollPaneResults.setViewportView(textAreaResults);

		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);

		JMenu menuFile = new JMenu("File");
		menuBar.add(menuFile);

		JMenuItem menuItemOpenModel = new JMenuItem("Open Model", IMG_FILE_OPEN);
		menuItemOpenModel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				try {
					openModel();
				} catch (Exception e) {
					LOG.error("Model open error!", e);
//					e.printStackTrace();
				}
			}
		});
		menuItemOpenModel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0));
		menuFile.add(menuItemOpenModel);

		JMenuItem menuItemOpenTest = new JMenuItem("Open Test", IMG_FILE_OPEN);
		menuItemOpenTest.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				try {
					openTest();
				} catch (Exception e) {
					LOG.error("Test open error!", e);
//					e.printStackTrace();
				}
			}
		});
		menuItemOpenTest.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0));
		menuFile.add(menuItemOpenTest);

		JMenuItem menuItemSaveModel = new JMenuItem("Save Model", IMG_FILE_SAVE);
		menuItemSaveModel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				try {
					saveModel();
				} catch (Exception e) {
					LOG.error("Model save error!", e);
//					e.printStackTrace();
				}
			}
		});
		menuItemSaveModel.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0));
		menuFile.add(menuItemSaveModel);

		JMenuItem menuItemSaveTest = new JMenuItem("Save Test", IMG_FILE_SAVE);
		menuItemSaveTest.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				try {
					saveTest();
				} catch (Exception e) {
					LOG.error("Test save error!", e);
//					e.printStackTrace();
				}
			}
		});
		menuItemSaveTest.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
		menuFile.add(menuItemSaveTest);

		JMenuItem menuItemExit = new JMenuItem("Exit", IMG_CLOSE);
		menuItemExit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				exit();
			}
		});
		menuItemExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK));
		menuFile.add(menuItemExit);

		JMenu menuHelp = new JMenu("Help");
		menuBar.add(menuHelp);

		JMenuItem menuItemCompInfo = new JMenuItem("Component Info", IMG_INFO);
		menuItemCompInfo.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				menuItemCompInfoAction();
			}
		});
		menuItemCompInfo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0));
		menuHelp.add(menuItemCompInfo);

		JMenuItem menuItemAbout = new JMenuItem("About", IMG_INFO);
		menuItemAbout.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				info();
			}
		});
		menuItemAbout.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
		menuHelp.add(menuItemAbout);

		LOG.debug("GUI was successfully initialized.");
	}

	private void btnCreateDModelAction() {
		String process = "Creating discrete model...";
		String errorMes = "";
		boolean result = false;
		long startTime = System.currentTimeMillis();
		try {
			// save data from GUI
			File tm = new File(TEMP_MODEL);
			File tt = new File(TEMP_TEST);
			contentProvider.saveFile(tm, textAreaModel.getText());
			contentProvider.saveFile(tt, textAreaTest.getText());

			startTime = System.currentTimeMillis();
			dModel = circuitLoader.getDModel(TEMP_MODEL);
			circuitTests = circuitTestLoader.getCircuitTest(circuitLoader.getSchemeEntryNum());

			tm.delete();
			tt.delete();

			btnModeling.setEnabled(true);
			buttonsActivation(3, false);

			result = true;
			LOG.debug("DModel was successfully created.");
		} catch (FileNotFoundException e) {
			errorMes = e.toString();
			buttonsActivation(2, false);
			LOG.error("File not found: " + e.getMessage(), e);
		} catch (DModelException e) {
			errorMes = e.toString();
			buttonsActivation(2, false);
			LOG.error("DModel wasn't created!", e);
		}
		setStatusInfo(result, startTime, process, errorMes);
	}

	private void btnModelingAction() {
		long startTime = System.currentTimeMillis();
		boolean modelingDone = false;
		String process = "Modeling...";
		String errorMes = "";
		File folder = new File(RESULT_FOLDER);
		if (contentProvider.isFolderExist(folder)) {
			modelingDone = Modeling.modeling(dModel, circuitTests, PROTOCOL_MODELING);
			if (modelingDone) {
				Element_Model.File_To_Diagr(dModel, PROTOCOL_ANALYSE);
				btnTimeChart.setEnabled(true);
				LOG.debug("Modeling was successfully done!");
			} else {
				errorMes = "Error in modeling module!";
				buttonsActivation(3, false);
				LOG.error(errorMes);
			}
		}
		setStatusInfo(modelingDone, startTime, process, errorMes);
	}

	private void btnTimeChartAction() {
		new TimeChartGUI(PROTOCOL_MODELING, PROTOCOL_ANALYSE);
	}

	private void buttonsActivation(int startIndex, boolean value) {
		switch (startIndex) {
		case 1:
			btnCreateDModel.setEnabled(value);
		case 2:
			btnModeling.setEnabled(value);
		case 3:
			btnTimeChart.setEnabled(value);
		case 4:
			btnResultsAnalize.setEnabled(value);
		case 5:
			btnSystemState.setEnabled(value);
		default:
			break;
		}

	}

	private void setWindowSize(int width, int height) {
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		setBounds((screenSize.width - width) / 2, (screenSize.height - height) / 2, width, height);
	}

	private void confFileChooser(JFileChooser fileChooser) {
		fileChooser.setFileFilter(new FileNameExtensionFilter("Text files", "txt"));
		fileChooser.setFileFilter(new FileNameExtensionFilter("XML files", "xml"));
		fileChooser.setCurrentDirectory(new File(System.getProperty("user.dir")));
	}

	private boolean openModel() throws Exception {
		boolean result = false;
		String data = (String) openFile(fileChooser);
		if (data != null) {
			textAreaModel.setText(data);
			result = true;
			LOG.debug("Model was successfully loaded.");
		}
		return result;
	}

	private boolean openTest() throws Exception {
		boolean result = false;
		String data = (String) openFile(fileChooser);
		if (data != null) {
			textAreaTest.setText(data);
			result = true;
			LOG.debug("Test was successfully loaded.");
		}
		return result;
	}

	private void saveModel() throws Exception {
		String data = textAreaModel.getText();
		saveFile(fileChooser, data);
		LOG.debug("Model was successfully saved.");
	}

	private void saveTest() throws Exception {
		String data = textAreaTest.getText();
		saveFile(fileChooser, data);
		LOG.debug("Test was successfully saved.");
	}

	private Object openFile(JFileChooser fileChooser) throws Exception {
		String data = null;
		fileChooser.setCurrentDirectory(getLastDir());
		int retVal = fileChooser.showOpenDialog(getContentPane());
		if (retVal == JFileChooser.APPROVE_OPTION) {
			File lastDir = fileChooser.getCurrentDirectory().getAbsoluteFile();
			setLastDir(lastDir);
			data = contentProvider.loadFile(fileChooser.getSelectedFile().getAbsoluteFile());
		}
		return data;
	}

	private void saveFile(JFileChooser fileChooser, String data) throws Exception {
		fileChooser.setCurrentDirectory(getLastDir());
		int retVal = fileChooser.showSaveDialog(getContentPane());
		if (retVal == JFileChooser.APPROVE_OPTION) {
			File lastDir = fileChooser.getCurrentDirectory().getAbsoluteFile();
			setLastDir(lastDir);
			contentProvider.saveFile(fileChooser.getSelectedFile().getAbsoluteFile(), data);
		}
	}

	/**
	 * Show status info and executing time.
	 * 
	 * @param result
	 * @param startTime
	 */
	private void setStatusInfo(boolean result, long startTime, String process, String errorMes) {
		long currentTime = System.currentTimeMillis() - startTime;
		String infoMessage = result ? "\nSuccess! No errors." : "\nError occured:\n" + errorMes;
		textAreaResults.setText(process + "\nTime is: " + (currentTime) + "ms" + infoMessage);
	}

	private void exit() {
		LOG.debug("Closing application...");
		Frame[] frames = Frame.getFrames();
		for (Frame fr : frames) {
			fr.dispose();
		}
		this.dispose();
		LOG.debug("Application closed successfully.");
		System.exit(0);
	}

	protected void menuItemCompInfoAction() {
		new ElemetsInfo();

	}

	private void info() {
		JOptionPane.showMessageDialog(getContentPane().getParent(),
				"APVS Project.\nAuthors:\n\tDiachenko Ruslan\n\tKopylov Alexander\n"
						+ "\tKopylova Anstasia\n\tLisitskaya Anastasia\n\tLopatin Daniil", "Info",
				JOptionPane.INFORMATION_MESSAGE);
	}

	public File getLastDir() {
		return lastDir;
	}

	public void setLastDir(File lastDir) {
		this.lastDir = lastDir;
	}
}
